import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;

public class Client extends UnicastRemoteObject implements Client_itf {

	private static final long serialVersionUID = 1L;
	private static Server_itf server;
	// Les sharedObject que le Client utilise.
	public static HashMap<Integer, SharedObject> map = new HashMap<Integer, SharedObject>();
	private static Client_itf c = null;

	public Client() throws RemoteException {
	}

	// /////////////////////////////////////////////////
	// Interface to be used by applications
	// /////////////////////////////////////////////////

	/**
	 * initialization of the client layer
	 * 
	 * initialise la couche cliente, a appeler au debut de l’application.
	 */
	public static void init() {
		System.out.println("initialisation du client : ");
		try {
			if (c == null) {
				c = new Client();
				String registryHost = "localhost";
				int registryPort = 6060;
				server = (Server_itf) Naming.lookup("//" + registryHost + ":"
						+ registryPort + "/Launcher");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Consulte le serveur de nom et retourne l’objet partage enregistre.
	 * 
	 * @param name
	 *            le nom d'objet qui existe dans le server de nom.
	 * @return retourner le SharedObject qui associe avec l'object.
	 */
	public static SharedObject lookup(String name) {
		SharedObject sharedObject = null;
		int id;
		try {
			// Appelé la méthode du Server. Il va retourner id d'objet. Si le
			// nom n'exsite pas dans le server de nom, il va retourner -1.
			id = server.lookup(name);
			if (id != -1) {
				// Construire un sharedObject par id et object qui associé avec
				// lui. Après on le stocke dans le Hashmap du Client.
				Object object = server.getObjectOfServerObject(id);
				sharedObject = new SharedObject(id, object);
				map.put(id, sharedObject);
			}
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return sharedObject;
	}

	/**
	 * enregistre un objet partagé dans le serveur de noms.
	 * 
	 * @param name
	 *            le nom d'objet que on veut stocker dans le server de nom.
	 * @param so
	 *            le sharedObject qui veut mettre son id dans le server de nom.
	 */
	public static void register(String name, SharedObject_itf so) {
		try {
			// Appeler la methode du server.
			server.register(name, so.getId());
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	/**
	 * créer un objet de sharedObject.
	 * 
	 * @param o
	 *            l'objet pour construire un objet de sharedObject.
	 * @return le sharedObject que on a créé.
	 */
	public static SharedObject create(Object o) {
		int id = 0;
		try {
			// Appeler la méthode du server. Il va retourner l'id que le server
			// distribue pour lui.
			id = server.create(o);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		SharedObject sharedObject = new SharedObject(id, o);
		// Mettre le nouveau objet dans le HashMap du Client.
		map.put(id, sharedObject);
		return sharedObject;
	}

	// ///////////////////////////////////////////////////////////
	// Interface to be used by the consistency protocol
	// //////////////////////////////////////////////////////////

	/**
	 * Demande d'un verrou en lecture au serveur.
	 * 
	 * @param id
	 *            l'identifiant unique de l'objet.
	 * @return le partagé objet qu'on lock.
	 */
	public static Object lock_read(int id) {
		Object o = null;
		try {
			o = server.lock_read(id, c);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return o;
	}

	/**
	 * appels locaux.------Request a write lock from the server------Demande
	 * d'un verrou en ecriture au serveur, en lui passant l'identifiant unique
	 * de l'objet (le SharedObject devait etre en NL ou RLC). Retourne l'etat de
	 * l'objet si le SharedObject etait en NL.
	 * 
	 * Ces méthodes (statiques) de la classe Client ne font que propager ces
	 * requêtes au serveur, en ajoutant aux paramètres de la requête une
	 * référence au client (instance) lorsque cela est nécessaire.
	 * 
	 */
	public static Object lock_write(int id) {
		Object o = null;
		try {
			o = server.lock_write(id, c);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return o;
	}

	// receive a lock reduction request from the server
	/**
	 * Appels a distance (RMI) Realize the interface of Client_itf
	 * 
	 * La référence au client reçue par le serveur lui permet de réclamer un
	 * verrou et une copie de l’objet au client qui la possède. L'interface du
	 * client (distante) permettant cette réclamation est la suivante :
	 * 
	 * permet au serveur de réclamer le passage d’un verrou de l’écriture à la
	 * lecture.
	 * 
	 * Une telle réclamation est propagée au descripteur d’objet concerné
	 * (SharedObject) sur le site client qui implante les mêmes méthodes:
	 */
	public Object reduce_lock(int id) throws java.rmi.RemoteException {
		return map.get(id).reduce_lock();
	}

	// receive a reader invalidation request from the server
	/**
	 * Appels a distance (RMI) Realize the interface of Client_itf
	 * 
	 * La référence au client reçue par le serveur lui permet de réclamer un
	 * verrou et une copie de l’objet au client qui la possède. L'interface du
	 * client (distante) permettant cette réclamation est la suivante : permet
	 * au serveur de réclamer l’invalidation d’un lecteur.
	 * 
	 * Une telle réclamation est propagée au descripteur d’objet concerné
	 * (SharedObject) sur le site client qui implante les mêmes méthodes:
	 */
	public void invalidate_reader(int id) throws java.rmi.RemoteException {
		map.get(id).invalidate_reader();
	}

	// receive a writer invalidation request from the server
	/**
	 * Appels a distance (RMI) Realize the interface of Client_itf
	 * 
	 * La référence au client reçue par le serveur lui permet de réclamer un
	 * verrou et une copie de l’objet au client qui la possède. L'interface du
	 * client (distante) permettant cette réclamation est la suivante :
	 * 
	 * permet au serveur de réclamer l’invalidation d’un écrivain.
	 * 
	 * Une telle réclamation est propagée au descripteur d’objet concerné
	 * (SharedObject) sur le site client qui implante les mêmes méthodes:
	 */
	public Object invalidate_writer(int id) throws java.rmi.RemoteException {
		return map.get(id).invalidate_writer();
	}
}
